/**
 * 捕获函数运行异常
 * 执行一个函数，返回结果或捕获的错误对象。
 * eg:
  attempt(function(selector:any) {
      return document.querySelectorAll(selector);
    }, '>_>')

 => DOMException: Failed to execute 'querySelectorAll' on 'Document': '>_>' is not a valid selector.....
 * @param func 捕获函数
 * @param args 参数
 * @return error
 */

export function attempt(func:Function,...args:any) {
  try {
    return func(...args);
  } catch (e) {
    // @ts-ignore
    return e instanceof Error ? e : new Error(e);
  }
}

/**
 * 防抖函数
 * eg:
 *    function func() { .... }
 *    debounce(func,300,false)
 *
 * 当事件触发完成之后再延迟触发，并且只触发一次，如果在触发完成之前再次触发，则会再次刷新延迟；
 * @param callBack 传入调用函数
 * @param delay 等待时间 | 默认为100毫秒
 * @param immediate 是否立即执行 | 默认为false
 */

export function debounce(callBack:Function, delay:number = 100,immediate:boolean=false) {
  let timer: any = null;
  return function () {
    // @ts-ignore
    let _this = this;
    let args = arguments; // 调用函数的参数
    if(timer) clearTimeout(timer);
    if(immediate) {
      let callNow = !timer;
      timer = setTimeout(() => {
        timer = null
      }, delay)
      if(callNow) {
        return callBack.apply(_this,args);
      }
    } else {
      timer = setTimeout(() => {
        callBack.apply(_this,args)
      },delay)
    }
  }
}


/**
 * 首节流
 * 第一次会马上执行，之后的操作不一定会执行。第一次执行，最后一次不执行。
 * eg:
 *    function func() { .... }
 *    beginningThrottle(func,200)
 *
 * @param callBack 传入调用函数
 * @param delay 等待时间 | 默认为100毫秒
 */

export function beginningThrottle(callBack:Function, delay: number = 100) {
  let last = 0;
  return function () {
    let now = Date.now();
    if(now - last > delay) {
      // @ts-ignore
      callBack.apply(this,arguments)
      last = now
    }
  }
}

/**
 * 尾节流
 * 第一次不会马上执行，而是一段时间后在执行。第一次不执行，最后一次执行。
 * eg:
 *     function func() { .... }
 *     beginningThrottle(func,200)
 *
 * @param callBack 传入调用函数
 * @param delay 等待时间 | 默认为100毫秒
 */
export function terminalThrottle(callBack:Function, delay: number = 100) {
  let timer:any = null;
  return function () {
    // @ts-ignore
    let _this = this;
    let args = arguments;
    if (!timer) {
      timer = setTimeout(function () {
        callBack.apply(_this, args);
        timer = null;
      }, delay);
    }
  }
}


/**
 * 兼容节流
 * 默认第一次会马上执行，最后一次也会执行。
 * eg:
 *     function func() { .... }
 *     beginningThrottle(func,200,true,true)
 *
 * leading = true, trailing = true => 第一次会执行，最后一次会执行
 * leading = false, trailing = true => 第一次不会执行，最后一次会执行
 * leading = true, trailing = false => 第一次会执行，最后一次不会执行
 * leading = false, trailing = false => 第一次不会执行，最后一次不会执行
 * @param callBack 传入调用函数
 * @param delay 等待时间 | 默认为100毫秒
 * @param leading 可选参数 | true
 * @param trailing 可选参数 | true
 */

export function firstAndEndOfThrottle(callBack:Function, delay: number = 100, leading:boolean = true,trailing:boolean = true) {
  let _this, args, timeout:any = null;
  let old = 0;     //时间戳

  let later = function () {
    old = new Date().valueOf();
    timeout = null;
    // @ts-ignore
    callBack.apply(this, arguments);
  }
  return function () {
    // @ts-ignore
    _this = this;
    args = arguments;
    let now = new Date().valueOf();
    if (!leading && !old) {
      old = now;
    }
    if (now - old > delay) {
      // 第一次直接执行
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      callBack.apply(_this, args);
      old = now;
    } else if (!timeout && trailing) {
      //最后一次也被执行
      timeout = setTimeout(later, delay)
    }
  }
}

/**
 * 推迟执行函数
 * eg:
 *  defer(console.log, 'a'), console.log('b'); // 'b' 'a'
 * @param callBack 调用函数
 * @param delay 时间
 * @param args 函数的参数
 */
export function defer(callBack:Function, delay:number = 100,...args:any) {
  return setTimeout(callBack,delay,...args)
}


// https://juejin.cn/post/6844903966526930951#heading-32
